﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace TestXNA3.Resources
{
    public class ResourceManager : ContentManager
    {
        public class ManagedResource<T>
        {
            public T Active;
            public T Original;
            public T Default;
            public string AssetName;
            public double LastTimeUsed;
            public IDisposable[] DisposableResources = null;
        }

        private List<IDisposable> _lastDisposables = new List<IDisposable>();

        private List<ManagedResource<Model>> _managedModels = new List<ManagedResource<Model>>();
        private List<ManagedResource<Texture>> _managedTextures = new List<ManagedResource<Texture>>();

        private Model _defaultModel = null;
        private Texture _defaultTexture = null;

        private double _lastUpdateTime = 0.0f;
        private double _updateEvery = 2.0f;

        public ResourceManager(IServiceProvider serviceProvider) : base(serviceProvider)
        {
        }

        public void Update(double dt, double currentTime)
        {
            if ((currentTime - _lastUpdateTime) < _updateEvery)
                return;

            _lastUpdateTime = currentTime;

            foreach (ManagedResource<Model> m in _managedModels)
            {
                CheckResource(m, currentTime);
            }

            foreach (ManagedResource<Texture> t in _managedTextures)
            {
                CheckResource(t, currentTime);
            }
        }

        private void recordDisposableObject(IDisposable disposable)
        {
            _lastDisposables.Add(disposable);
        }

        public ManagedResource<Model> LoadManagedModel(string assetName)
        {
            if (_defaultModel == null)
            {
                _defaultModel = base.Load<Model>("StaticMeshes\\cube");
            }
            ManagedResource<Model> asset = LoadManaged<Model>(assetName, _defaultModel);
            _managedModels.Add(asset);
            return asset;
        }

        public ManagedResource<Texture> LoadManagedTexture(string assetName)
        {
            if (_defaultTexture == null)
            {
                _defaultTexture = base.Load<Texture>("Textures\\default");
            }
            ManagedResource<Texture> asset = LoadManaged<Texture>(assetName, _defaultTexture);
            _managedTextures.Add(asset);
            return asset;
        }

        private ManagedResource<T> LoadManaged<T>(string assetName, T defaultResource)
        {
            ManagedResource<T> asset = new ManagedResource<T>();
            asset.Default = defaultResource;

            try
            {
                asset.Original = ReadAsset<T>(assetName, recordDisposableObject);
                asset.DisposableResources = new IDisposable[_lastDisposables.Count];
                _lastDisposables.CopyTo(asset.DisposableResources);
                _lastDisposables.Clear();
                asset.AssetName = assetName;
                asset.Active = asset.Original;
            }
            catch
            {
                asset.Original = asset.Default;
                asset.Active = asset.Original;
                asset.DisposableResources = null;
            }

            asset.LastTimeUsed = GameStartup.StaticGameStartup.GameInfo.TimeSeconds;
            return asset;
        }

        private void CheckResource<T>(ManagedResource<T> asset, double currentTime)
        {
            double lastTimeDiff = Math.Abs(asset.LastTimeUsed - currentTime);
            if (asset.Original == null && lastTimeDiff < 1.0f)
            {
                try
                {
                    asset.Original = ReadAsset<T>(asset.AssetName, recordDisposableObject);
                    asset.DisposableResources = new IDisposable[_lastDisposables.Count];
                    _lastDisposables.CopyTo(asset.DisposableResources);
                    _lastDisposables.Clear();
                    asset.Active = asset.Original;
                }
                catch
                {
                    asset.Original = default(T);
                    asset.AssetName = "";
                    asset.Active = asset.Default;
                }
            }else
            if (asset.Original != null && lastTimeDiff > 10.0f)
            {
                asset.Active = asset.Default;
                asset.Original = default(T);
                foreach (IDisposable d in asset.DisposableResources)
                {
                    d.Dispose();
                }
                asset.DisposableResources = null;
            }
        }
    }
}
